<script setup>
import {computed, inject, provide, reactive, ref, useAttrs} from 'vue'
import {NButton, NModal} from 'naive-ui'
import {JBoltApi} from '@/service/request'
import JbIcon from '@/components/_builtin/jb-icon/index.vue'
import {Icons} from '@/constants'

const props = defineProps({
    confirmText: String,
    url: String,
    icon: {
        type: String,
        default: Icons.ADD
    },
    tipText: String,
    size: {
        type: String,
        validator: (value) => ['tiny', 'small', 'medium', 'large'].includes(value)
    },
    /**
     * 如果点击后显示弹窗，这里传入弹窗组件
     */
    modalComponent: Object,
    /**
     * 弹窗组件要显示的html
     */
    modalHtml: String,
    /**
     * 弹窗组件需要接收的参数
     */
    modalComponentProps: {
        type: Object,
        default: () => ({})
    },
    /**编辑弹窗宽度*/
    modalWidth: {
        type: String,
        default: '800px'
    },
    /**
     * 弹窗标题
     */
    modalTitle: String,
    /**
     * 弹窗参数，参考naive-ui的modal组件
     */
    modalArgs: {
        type: Object,
        default: () => ({})
    },
    /**
     * 如果点击后显示抽屉，这里传入弹窗组件
     */
    drawerComponent: Object,
    /**
     * 抽屉组件要显示的html
     */
    drawerHtml: String,
    /**
     * 抽屉标题
     */
    drawerTitle: String,
    /**
     * 抽屉参数，参考naive-ui的modal组件
     */
    drawerArgs: {
        type: Object,
        default: () => ({})
    },
    /**
     * 抽屉组件需要接收的参数
     */
    drawerComponentProps: {
        type: Object,
        default: () => ({})
    },
    /** 抽屉的确认按钮文字，为空则不显示该按钮 */
    drawerPositiveText: {
        type: String,
        default: '确认'
    },
    /** 抽屉的确认按钮样式，参考n-button的type，为空则不显示该按钮 */
    drawerPositiveType: {
        type: String,
        default: 'primary'
    },
    /** 抽屉的取消按钮文字，为空则不显示该按钮 */
    drawerNegativeText: {
        type: String,
        default: '关闭'
    },
    /** 抽屉的确认按钮样式，参考n-button的type，为空则不显示该按钮 */
    drawerNegativeType: {
        type: String,
        default: 'default'
    },
    /** 是否是一个超链接按钮 */
    isLinkBtn: {
        type: Boolean,
        default: false
    },
    /** 是否是一个下载按钮,如果传入字符串，则会用该字符串作为文件名下载，如果传入true，则会用连接地址的默认文件名保存 */
    download: {
        type: [String, Boolean]
    },
    /** 下载请求附带的参数 */
    downloadForm: Object,
    /** 是否是一个上传按钮 */
    upload: Boolean,
    /** 上传的字段名，默认为file */
    uploadFieldName: {
        type: String,
        default: 'file'
    },
    /** 上传接受的文件类型 */
    uploadAccept: String,
    /** 上传请求附带的参数 */
    uploadForm: Object
})

const attrs = useAttrs()
const emit = defineEmits([
    'confirm',
    'cancel',
    'success',
    'error',
    'modalYesClick',
    'modalNoClick',
    'drawerYesClick',
    'drawerNoClick',
    'drawerMaskClick',
    'uploadSuccess',
    'uploadFail',
    'uploadComplete'
])

defineOptions({
    inheritAttrs: false
})

const state = reactive({
    loading: false,
    modalShown: false,
    drawerShown: false
})

const currentTable = inject('currentTable', null)

const size = computed(() => {
    //table内的button 默认 small size
    return props.size ?? currentTable ? 'small' : 'medium'
})

/** 弹窗相关 start */
const modalRef = ref(null)
const modalComponentRef = ref(null)

const modalArgs = computed(() => {
    return Object.assign(
        {},
        {
            positiveText: '确认',
            negativeText: '取消',
            maskClosable: false,
        },
        props.modalArgs,
        {
            title: props.modalTitle,
            style: Object.assign({}, props.modalArgs?.style, {
                width: props.modalWidth
            })
        }
    )
})

function showModal() {
    state.modalShown = true
}

function handleModalYesClick() {

    console.debug(`点击了弹窗的确认按钮`)
    try {
        emit('modalYesClick', {
            close: closeModal,
            componentRef: modalComponentRef.value,
            componentProps: props.modalComponentProps
        })
    } catch (error) {
        console.error(error)
    }

    return false
}

function handleModalNoClick() {
    try {
        emit('modalNoClick', {
            close: closeModal,
            componentRef: modalComponentRef.value,
            componentProps: props.modalComponentProps
        })
    } catch (error) {
        return false
    }
}

function closeModal() {
    state.modalShown = false
}

/** 弹窗相关 end */

/** 抽屉相关 start */
const drawerComponentRef = ref(null)
const drawerArgs = computed(() => {
    return Object.assign(
        {},
        {
            defaultWidth: '800px',
            resizable: true,
        },
        props.drawerArgs
    )
})

function showDrawer() {
    state.drawerShown = true
}

function closeDrawer() {
    state.drawerShown = false
}

function handleDrawerYesClick() {
    try {
        emit('drawerYesClick', {
            close: closeDrawer,
            componentRef: drawerComponentRef.value,
            componentProps: props.drawerComponentProps
        })
    } catch (error) {
        console.error(error)
    }

    return false
}

function handleDrawerNoClick() {
    try {
        emit('drawerNoClick', {
            close: closeDrawer,
            componentRef: drawerComponentRef.value,
            componentProps: props.drawerComponentProps
        })
        closeDrawer()
    } catch (error) {
        return false
    }
}

function handleMaskClick() {
    try {

        emit('drawerMaskClick', {
            close: closeDrawer,
            componentRef: drawerComponentRef.value,
            componentProps: props.drawerComponentProps
        })
        if (props.drawerArgs.maskClosable === null || props.drawerArgs.maskClosable === undefined || props.drawerArgs.maskClosable ===true) {
            closeDrawer()
        }
    } catch (error) {
        return false
    }
}

/** 抽屉相关 end */

/**
 * 点了确认
 */
function doConfirm() {
    execute()
    emit('confirm')
}

/**
 * 执行下载操作
 */
function executeDownload() {
    if (!props.url) return
    let fileName = ''
    if (typeof props.download == 'string') {
        fileName = props.download
    }
    JBoltApi.download(props.url, fileName, props.downloadForm)
}

const uploadFile = ref()

/**
 * 当上传的文件发生变动
 * @param e
 */
function onUploadFileChange(e) {
    const files = e.target.files
    if (!files || !files[0]) return
    state.loading = true
    JBoltApi.post(
        props.url,
        {
            [props.uploadFieldName]: files[0],
            ...props.uploadForm
        },
        {
            headers: {
                'Content-Type': 'multipart/form-data'
            }
        }
    ).then((res) => {
        state.loading = false
        e.target.value = null
        emit('uploadSuccess', res.result)
        emit('uploadFail', res.error)
        emit('uploadComplete', res)
    })
}

/**
 * 执行上传操作
 */
function executeUpload() {
    if (!props.url) return
    uploadFile.value.click()
}

async function execute() {
    if (props.url && props.isLinkBtn) {
        //超链接按钮
        window.open(props.url)
    } else if (props.url && props.download) {
        executeDownload()
    } else if (props.url && props.upload) {
        executeUpload()
    } else if (props.url) {
        sendRequest()
    } else if (props.modalComponent || props.modalHtml) {
        showModal()
    } else if (props.drawerComponent || props.drawerHtml) {
        showDrawer()
    }
}

/**
 * 确认点了取消
 */
function doCancel() {
    emit('cancel')
}

/**
 * 处理 点击事件
 */
function handleClick() {
    event?.stopPropagation()
    if (attrs.onClick) {
        attrs.onClick()
        return
    }
    //如果是确认按钮，不执行点击事件
    if (props.confirmText) {
        return
    }
    execute()
}

/**
 * 根据url发送请求
 */
async function sendRequest() {
    if (props.url) {
        if (state.loading) return
        state.loading = true
        let {error, result} = await JBoltApi.get(props.url)

        state.loading = false
        if (error) {
            emit('error', result)
            return
        }
        emit('success', result)
    }
}

/**
 * 将属性中的onclick过滤掉，因为如果有onclick，会覆盖掉我们的点击事件
 */
const validAttrs = computed(() => {
    const validAttrs = {...attrs}
    delete validAttrs.onClick
    return validAttrs
})

defineExpose({
    closeModal,
    showDrawer,
    closeDrawer,
    showModal
})
provide('closeDrawer', closeDrawer)
provide('closeModal', closeModal)
</script>

<template>
    <div style="display: inline-flex" class="jb-btn">
        <!-- 既有提示信息，又有确认信息，提示信息显示在按钮上 -->
        <n-tooltip
            v-if="props.tipText && props.confirmText"
            trigger="hover"
            placement="right"
        >
            <template #trigger>
                <n-popconfirm @positive-click="doConfirm()" @negative-click="doCancel()">
                    <template #trigger>
                        <n-button
                            :size="size"
                            v-bind="validAttrs"
                            :loading="state.loading"
                            @click="handleClick"
                        >
                            <template #default>
                                <slot></slot>
                            </template>
                            <template #icon>
                                <jb-icon :local="true" v-if="props.icon" :icon="props.icon"></jb-icon>
                            </template>
                        </n-button>
                    </template>
                    {{ props.confirmText }}
                </n-popconfirm>
            </template>
            {{ props.tipText }}
        </n-tooltip>
        <!-- 只有提示信息，提示信息显示在按钮上 -->
        <n-tooltip v-else-if="props.tipText" trigger="hover">
            <template #trigger>
                <n-button
                    v-bind="validAttrs"
                    :size="size"
                    :loading="state.loading"
                    @click="handleClick"
                >
                    <template #default>
                        <slot></slot>
                    </template>
                    <template #icon>
                        <jb-icon :local="true" v-if="props.icon" :icon="props.icon"></jb-icon>
                    </template>
                </n-button>
            </template>
            {{ props.tipText }}
        </n-tooltip>
        <!-- 只有确认信息 -->
        <n-popconfirm
            v-else-if="props.confirmText"
            @positive-click="doConfirm()"
            @negative-click="doCancel()"
        >
            <template #trigger>
                <n-button
                    v-bind="validAttrs"
                    :size="size"
                    :loading="state.loading"
                    @click="handleClick"
                >
                    <template #default>
                        <slot></slot>
                    </template>
                    <template #icon>
                        <jb-icon :local="true" v-if="props.icon" :icon="props.icon"></jb-icon>
                    </template>
                </n-button>
            </template>
            {{ props.confirmText }}
        </n-popconfirm>
        <!--什么都没有 -->
        <n-button
            v-else
            v-bind="validAttrs"
            :size="size"
            :loading="state.loading"
            @click="handleClick"
        >
            <template #default>
                <slot></slot>
            </template>
            <template #icon>
                <jb-icon :local="true" v-if="props.icon" :icon="props.icon"></jb-icon>
            </template>
        </n-button>

        <n-modal
            v-if="props.modalComponent || props.modalHtml"
            ref="modalRef"
            v-model:show="state.modalShown"
            preset="dialog"
            v-bind="modalArgs"
            @positive-click="handleModalYesClick"
            @negative-click="handleModalNoClick"
        >
            <component
                :is="props.modalComponent"
                v-if="props.modalComponent"
                ref="modalComponentRef"
                v-bind="props.modalComponentProps"
            ></component>
            <div
                v-else-if="props.modalHtml"
                class="html_wrapper"
                v-html="props.modalHtml"
            ></div>
        </n-modal>

        <n-drawer
            :trap-focus="false"
            v-if="props.drawerComponent || props.drawerHtml"
            v-model:show="state.drawerShown"
            v-bind="drawerArgs"
            @mask-click="handleMaskClick"
        >
            <n-drawer-content
                :title="props.drawerTitle"
                :native-scrollbar="false"
                class="jb-btn_drawer"
            >
                <component
                    :is="props.drawerComponent"
                    v-if="props.drawerComponent"
                    ref="drawerComponentRef"
                    v-bind="props.drawerComponentProps"
                ></component>
                <div
                    v-else-if="props.drawerHtml"
                    class="html_wrapper"
                    v-html="props.drawerHtml"
                ></div>
                <template #footer v-if="props.drawerNegativeText!=''&&props.drawerPositiveText!=''">
                    <n-space justify="end">
                        <n-button
                            v-if="props.drawerPositiveText"
                            :type="props.drawerPositiveType"
                            @click="handleDrawerYesClick()"
                        >{{ props.drawerPositiveText }}
                        </n-button
                        >
                        <n-button
                            v-if="props.drawerNegativeText"
                            :type="props.drawerNegativeType"
                            @click="handleDrawerNoClick()"
                        >{{ props.drawerNegativeText }}
                        </n-button
                        >
                    </n-space>
                </template>
            </n-drawer-content>
        </n-drawer>

        <input
            ref="uploadFile"
            type="file"
            style="display: none"
            :accept="props.uploadAccept"
            @change="onUploadFileChange($event)"
        />
    </div>
</template>

<style scoped>
.jb-btn_drawer :deep(.n-drawer-body-content-wrapper) {
    height: 100%;
}
</style>
<style>
.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
.jb-btn:first-child:not(:last-child)
.n-button {
    margin-right: 0 !important;
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
.jb-btn:not(:first-child):not(:last-child)
.n-button {
    margin-left: 0 !important;
    margin-right: 0 !important;
    border-radius: 0 !important;
}

.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
.jb-btn:last-child:not(:first-child)
.n-button {
    margin-left: 0 !important;
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}

.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
.jb-btn:not(:first-child) {
    margin-left: -1px !important;
}

.html_wrapper img,
.html_wrapper video {
    max-width: 98%;
}
</style>
